package com.geek.framework.study;


import com.geek.framework.study.config.anno.ResourceConfigSource;
import com.geek.framework.study.config.anno.ResourceConfigSourcesParser;
import com.geek.framework.study.config.microprofile.converter.BooleanConverter;
import com.geek.framework.study.config.microprofile.converter.StringConversionService;
import com.geek.framework.study.config.microprofile.converter.StringConvert;
import org.eclipse.microprofile.config.Config;
import org.eclipse.microprofile.config.ConfigProvider;
import org.eclipse.microprofile.config.ConfigValue;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.eclipse.microprofile.config.spi.ConfigProviderResolver;
import org.eclipse.microprofile.config.spi.ConfigSource;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

import javax.inject.Inject;
import java.lang.reflect.*;
import java.util.Optional;
import java.util.stream.Stream;

import static org.eclipse.microprofile.config.inject.ConfigProperty.UNCONFIGURED_VALUE;


/**
 * {@link com.geek.framework.study.config.anno.ResourceConfigSource}
 * {@link com.geek.framework.study.config.anno.ResourceConfigSources}
 * {@link java.lang.annotation.Repeatable Java 8 @Repeatable 可以实现注解多次使用 }
 *
 */
@ResourceConfigSource(name = "configDemo", value = {"fullpath:D:\\CodeStudy\\geek-spring\\homework\\section06\\src\\test\\resources\\META-INF\\test-config.properties"})
@ResourceConfigSource(name = "doubleConfig", value = {"fullpath:D:\\CodeStudy\\geek-spring\\homework\\section06\\src\\test\\resources\\META-INF\\test-config-2.properties"})
public class ConfigDemo {

    @Inject
    @ConfigProperty(name = "greeting.message",defaultValue = "Hello MicroProfile Config, default value")
    private Optional<String> message;

    private Config config;
    
    @Before
    public void mockConfigPropertyInject() throws Throwable {
        initConfig();
        mockConfigPropertyInjectForMessage();
    }

    private void initConfig() {
//        initConfigSources();
        this.config = ConfigProvider.getConfig();
    }

    private void initConfigSources() {
        System.setProperty("greeting.message", "Hello MicroProfile Config 2");
    }

    @Test
    public void configPropertyTest() {
        System.out.println("message = " + message);
    }

    private void mockConfigPropertyInjectForMessage() throws Throwable {
        final Class<?> beanClass = getClass();
        Field[] configPropertyFields = findConfigPropertyFields(beanClass);
        Field field = Stream.of(configPropertyFields)
                .filter(f -> "message".equals(f.getName()))
                .findFirst()
                .orElse(null);
        final Type fieldType = field.getGenericType();
        Object fieldValue = null;
        if (fieldType instanceof ParameterizedType) {
            ParameterizedType fieldParamType = (ParameterizedType) fieldType;
            Class containerType = (Class) fieldParamType.getRawType();// 获取原始类型
            Class convertedType = (Class) fieldParamType.getActualTypeArguments()[0];//获取实际类型参数
            ConfigProperty configProperty = field.getAnnotation(ConfigProperty.class);
            String propertyeName = configProperty.name();
            String defaultValue = configProperty.defaultValue();
            String message = config.getValue(propertyeName, String.class);
            if (Optional.class.equals(containerType)) {

                if (message == null && !UNCONFIGURED_VALUE.equals(defaultValue)) {
                    message = defaultValue;
                }

                fieldValue = Optional.ofNullable(message);
            }

            if (fieldValue != null) {
                // inject
                field.setAccessible(true);
                field.set(this, fieldValue);
            }
        }
    }

    private Field[] findConfigPropertyFields(final Class<?> beanClass) {
        final Field[] allFields = beanClass.getDeclaredFields();
        // filter condition:
        // 1.non-static
        // 2.non-final
        // 3.annotated @javax.inject.Inject
        // 4.annotated @org.eclipse.microprofile.config.inject.ConfigProperty
        return Stream.of(allFields)
                .filter(field -> {
                    int mods = field.getModifiers();
                    return !Modifier.isStatic(mods)
                            && !Modifier.isFinal(mods);
                })
                .filter(field -> field.isAnnotationPresent(Inject.class)
                        && field.isAnnotationPresent(ConfigProperty.class))
                .toArray(Field[]::new);
    }

    /**
     * 类型转行代码补全
     * @see StringConversionService
     */
    @Test
    public void configTest() {
        Config config = ConfigProvider.getConfig();
        ConfigValue abc = config.getConfigValue("abc");
        System.out.println("abc.getValue() = " + abc.getValue());

        Integer configValue = config.getValue("abc", Integer.class);
        System.out.println("configValue = " + configValue);

        System.out.println("greeting.message = " + config.getConfigValue("greeting.message").getValue());

    }


    /**
     * 文件路径根据电脑位置进行指定
     */
    @Test
    public void ResourceConfigTest() {
        ResourceConfigSourcesParser parser = new ResourceConfigSourcesParser();
        parser.register(ConfigDemo.class);

        ConfigProviderResolver configProviderResolver = ConfigProviderResolver.instance();

        Config config = configProviderResolver.getBuilder()
                .addDefaultSources()
                .addDiscoveredConverters()
                .withSources(parser.parse().toArray(new ConfigSource[]{}))
                .withConverters(new StringConvert(),new BooleanConverter())
                .build();
        String username = config.getValue("hello", String.class);
        Assert.assertEquals("world", username);
        System.out.println("username = " + username);
        
        Boolean openSource = config.getValue("openSource", Boolean.class);
        System.out.println("openSource = " + openSource);

        final String config2Value = config.getValue("config-2", String.class);
        Assert.assertEquals(config2Value,"demo-2");
        System.out.println("config-2 = " + config2Value);
    }

}
